#ifndef __POINT_H__
#define __POINT_H__


#include "iostream"


/**
 * 1. 将类的声明和实现放在一个文件中
 * 2. 类前添加模板声明
 * 3. 每一个类外函数需要添加函数说明
 * 4. 类需要指定模板 Point<T>
 * 5. 由于只有一个源码文件，常使用 .hpp 结尾命名类模板文件
 * 6. 类模板中使用友元函数时需要单独使用模板（声明和定义分开时，如本程序示例）
*/
template<typename T>
class Point
{
    template<typename U>
    friend std::ostream& operator<<(std::ostream& out, const Point<U>& p);
public:
    Point(T x = 0, T y = 0);
    ~Point();

    Point& operator=(const Point& p);
    T operator[](int index);
    Point operator+(const Point& p);     // +
    Point& operator++();    // 前置 ++
    Point operator++(int);  // 后置 ++
    Point& operator--();    // 前置 --
    Point operator--(int);  // 后置 --
    Point& operator-();     // 负号
private:
    T x;
    T y;
};


template<typename T>
Point<T>::Point(T x, T y)
    : x(x)
    , y(y)
{
    std::cout << "Point()" << std::endl;
}

template<typename T>
Point<T>::~Point()
{
    std::cout << "~Point()" << std::endl;
}

template<typename T>
std::ostream& operator<<(std::ostream& out, const Point<T>& p)
{
    out << "Point(" << p.x << ", " << p.y << ")" << std::endl;
    return out;
}

template<typename T>
Point<T>& Point<T>::operator=(const Point<T>& p)
{
    std::cout << "operator=(const Point& p)" << std::endl;
    // this->x = p.x;
    // this->y = p.y;
    // 判断地址相同表示同一个对象，不必赋值
    if(this != &p) {
        this->x = p.x;
        this->y = p.y;
    }
    return *this;
}

template<typename T>
T Point<T>::operator[](int index)
{
    if(index < 0 || index > 1)
        throw std::out_of_range("index out of range, index range in [0, 1].");
    if(0 == index)
        return this->x;
    if(1 == index)
        return this->y;
}

template<typename T>
Point<T> Point<T>::operator+(const Point& p)
{
    // 在运算符重载函数中构造一个临时对象并返回一个对象。

    // 由于临时对象在函数内有效，出了该函数后将再次通过拷贝构造一个临时对象。
    // Point temp;
    // temp.x = this->x + p.x;
    // temp.y = this->y + p.y;
    // return temp;

    return Point(this->x + p.x, this->y + p.y);
}

template<typename T>
Point<T>& Point<T>::operator++()
{
    ++this->x;
    ++this->y;
    return *this;
}

template<typename T>
Point<T> Point<T>::operator++(int)
{
    Point temp(*this);
    operator++();
    return temp;
}

template<typename T>
Point<T>& Point<T>::operator--()
{
    --this->x;
    --this->y;
    return *this;
}

template<typename T>
Point<T> Point<T>::operator--(int)
{
    Point temp(*this);
    operator--();
    return temp;
}

template<typename T>
Point<T>& Point<T>::operator-()
{
    this->x = -this->x;
    this->y = -this->y;
    return *this;
}

#endif